/*
 * Copyright (C) 2012 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package retrofit2;

import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

import javax.annotation.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

import static java.util.Collections.unmodifiableList;

/**
 * Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to
 * define how requests are made. Create instances using {@linkplain Builder the builder} and pass
 * your interface to {@link #create} to generate an implementation.
 *
 * <p>For example,
 *
 * <pre><code>
 * Retrofit retrofit = new Retrofit.Builder()
 *     .baseUrl("https://api.example.com/")
 *     .addConverterFactory(GsonConverterFactory.create())
 *     .build();
 *
 * MyApi api = retrofit.create(MyApi.class);
 * Response&lt;User&gt; user = api.getUser().execute();
 * </code></pre>
 *
 * @author Bob Lee (bob@squareup.com)
 * @author Jake Wharton (jw@squareup.com)
 */
public final class Retrofit {
    /**
     * 接口方法信息的缓存
     */
    private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();

    /**
     * OkHttpClient
     */
    final okhttp3.Call.Factory callFactory;
    /**
     * 基础Url
     */
    final HttpUrl baseUrl;
    /**
     * Converter转换器工厂列表
     */
    final List<Converter.Factory> converterFactories;
    /**
     * CallAdapter适配器工厂列表
     */
    final List<CallAdapter.Factory> callAdapterFactories;
    /**
     * 回调执行器
     */
    final @Nullable
    Executor callbackExecutor;
    /**
     * 是否创建接口实例时就校验接口的合法性，默认为false，在调用接口的方法时，才校验
     */
    final boolean validateEagerly;

    Retrofit(
            okhttp3.Call.Factory callFactory,
            HttpUrl baseUrl,
            List<Converter.Factory> converterFactories,
            List<CallAdapter.Factory> callAdapterFactories,
            @Nullable Executor callbackExecutor,
            boolean validateEagerly) {
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
        this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
        this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
        this.callbackExecutor = callbackExecutor;
        this.validateEagerly = validateEagerly;
    }

    /**
     * 传入一个API接口类的Class，动态代理该接口的实例
     */
    @SuppressWarnings("unchecked")
    public <T> T create(final Class<T> service) {
        //检查接口中的方法的合法性
        validateServiceInterface(service);
        return (T) Proxy.newProxyInstance(
                service.getClassLoader(),
                new Class<?>[]{service},
                new InvocationHandler() {
                    private final Platform platform = Platform.get();
                    private final Object[] emptyArgs = new Object[0];

                    @Override
                    public @Nullable
                    Object invoke(Object proxy, Method method, @Nullable Object[] args)
                            throws Throwable {
                        //如果调用的是Object的方法，直接调用，不需要处理
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        //没有参数，也用一个空数组
                        args = args != null ? args : emptyArgs;
                        //如果是default默认方法，直接调用
                        return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args)
                                //普通方法，进行加载和解析，然后进行调用
                                : loadServiceMethod(method).invoke(args);
                    }
                });
    }

    /**
     * 检查API接口Class
     */
    private void validateServiceInterface(Class<?> service) {
        //传入的Class必须是接口类
        if (!service.isInterface()) {
            throw new IllegalArgumentException("API declarations must be interfaces.");
        }

        Deque<Class<?>> check = new ArrayDeque<>(1);
        check.add(service);
        while (!check.isEmpty()) {
            Class<?> candidate = check.removeFirst();
            if (candidate.getTypeParameters().length != 0) {
                StringBuilder message =
                        new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
                if (candidate != service) {
                    message.append(" which is an interface of ").append(service.getName());
                }
                throw new IllegalArgumentException(message.toString());
            }
            Collections.addAll(check, candidate.getInterfaces());
        }
        //是否创建的时候就校验，默认为false，当调用接口方法时才校验，如果配置为true，则创建接口代理实现类时就校验
        if (validateEagerly) {
            Platform platform = Platform.get();
            //遍历接口上的方法
            for (Method method : service.getDeclaredMethods()) {
                //不检查default默认方法和静态方法
                if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
                    loadServiceMethod(method);
                }
            }
        }
    }

    /**
     * 加载接口方法
     */
    ServiceMethod<?> loadServiceMethod(Method method) {
        //优先从缓存中取，取得到则返回，不需要每次调用都处理一次，浪费性能
        ServiceMethod<?> result = serviceMethodCache.get(method);
        if (result != null) {
            return result;
        }
        //Double Check + 同步锁，确保多线程调用时真的找不到缓存
        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                //真的没有，解析方法上的注解
                result = ServiceMethod.parseAnnotations(this, method);
                //解析完，放到缓存
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }

    /**
     * 获取配置的OkHttpClient
     */
    public okhttp3.Call.Factory callFactory() {
        return callFactory;
    }

    /**
     * 获取配置的基础Url
     */
    public HttpUrl baseUrl() {
        return baseUrl;
    }

    /**
     * 返回注册的CallAdapter工厂
     */
    public List<CallAdapter.Factory> callAdapterFactories() {
        return callAdapterFactories;
    }

    /**
     * 返回可以适配返回值类型的CallAdapter实例
     *
     * @throws IllegalArgumentException 如果没有找到，抛出异常
     */
    public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
        return nextCallAdapter(null, returnType, annotations);
    }

    /**
     * 返回可以适配返回值类型的CallAdapter实例
     *
     * @throws IllegalArgumentException 如果没有找到，抛出异常
     */
    public CallAdapter<?, ?> nextCallAdapter(
            @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
        Objects.requireNonNull(returnType, "returnType == null");
        Objects.requireNonNull(annotations, "annotations == null");

        int start = callAdapterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
            CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
            if (adapter != null) {
                return adapter;
            }
        }

        StringBuilder builder =
                new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n");
        if (skipPast != null) {
            builder.append("  Skipped:");
            for (int i = 0; i < start; i++) {
                builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
            }
            builder.append('\n');
        }
        builder.append("  Tried:");
        for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
            builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
        }
        throw new IllegalArgumentException(builder.toString());
    }

    /**
     * 获取注册的Converter工厂
     */
    public List<Converter.Factory> converterFactories() {
        return converterFactories;
    }

    /**
     * 获取Http是json请求时，模型数据转换为json，再设置到RequestBody的转换器
     */
    public <T> Converter<T, RequestBody> requestBodyConverter(
            Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
        return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
    }

    /**
     * 获取Http是json请求时，模型数据转换为json，再设置到RequestBody的转换器
     *
     * @param skipPast 支持跳过指定的转换器
     */
    public <T> Converter<T, RequestBody> nextRequestBodyConverter(
            @Nullable Converter.Factory skipPast,
            Type type,
            Annotation[] parameterAnnotations,
            Annotation[] methodAnnotations) {
        Objects.requireNonNull(type, "type == null");
        Objects.requireNonNull(parameterAnnotations, "parameterAnnotations == null");
        Objects.requireNonNull(methodAnnotations, "methodAnnotations == null");

        int start = converterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = converterFactories.size(); i < count; i++) {
            Converter.Factory factory = converterFactories.get(i);
            Converter<?, RequestBody> converter = factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
            if (converter != null) {
                //noinspection unchecked
                return (Converter<T, RequestBody>) converter;
            }
        }

        StringBuilder builder =
                new StringBuilder("Could not locate RequestBody converter for ").append(type).append(".\n");
        if (skipPast != null) {
            builder.append("  Skipped:");
            for (int i = 0; i < start; i++) {
                builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
            }
            builder.append('\n');
        }
        builder.append("  Tried:");
        for (int i = start, count = converterFactories.size(); i < count; i++) {
            builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
        }
        throw new IllegalArgumentException(builder.toString());
    }

    /**
     * 获取支持返回值中的泛型为ResponseBody的Converter转换器
     */
    public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
        return nextResponseBodyConverter(null, type, annotations);
    }

    /**
     * 获取支持返回值中的泛型为ResponseBody的Converter转换器
     *
     * @param skipPast 可以指定跳过某个转换器
     */
    public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
            @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
        Objects.requireNonNull(type, "type == null");
        Objects.requireNonNull(annotations, "annotations == null");

        int start = converterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = converterFactories.size(); i < count; i++) {
            Converter<ResponseBody, ?> converter =
                    converterFactories.get(i).responseBodyConverter(type, annotations, this);
            if (converter != null) {
                //noinspection unchecked
                return (Converter<ResponseBody, T>) converter;
            }
        }

        StringBuilder builder =
                new StringBuilder("Could not locate ResponseBody converter for ")
                        .append(type)
                        .append(".\n");
        if (skipPast != null) {
            builder.append("  Skipped:");
            for (int i = 0; i < start; i++) {
                builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
            }
            builder.append('\n');
        }
        builder.append("  Tried:");
        for (int i = start, count = converterFactories.size(); i < count; i++) {
            builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
        }
        throw new IllegalArgumentException(builder.toString());
    }

    /**
     * 获取支持返回值的泛型是String的Converter转换器
     */
    public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
        Objects.requireNonNull(type, "type == null");
        Objects.requireNonNull(annotations, "annotations == null");

        for (int i = 0, count = converterFactories.size(); i < count; i++) {
            Converter<?, String> converter =
                    converterFactories.get(i).stringConverter(type, annotations, this);
            if (converter != null) {
                //noinspection unchecked
                return (Converter<T, String>) converter;
            }
        }

        // Nothing matched. Resort to default converter which just calls toString().
        //noinspection unchecked
        return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
    }

    /**
     * 返回配置的回调执行器
     */
    public @Nullable
    Executor callbackExecutor() {
        return callbackExecutor;
    }

    /**
     * 拷贝Retrofit实例数据，返回一个新的Builder
     */
    public Builder newBuilder() {
        return new Builder(this);
    }

    /**
     * 使用Builder构建者模式
     */
    public static final class Builder {
        /**
         * 平台
         */
        private final Platform platform;
        /**
         * OkHttpClient
         */
        private @Nullable
        okhttp3.Call.Factory callFactory;
        /**
         * 基础路径
         */
        private @Nullable
        HttpUrl baseUrl;
        /**
         * 注册的Converter转换器工厂
         */
        private final List<Converter.Factory> converterFactories = new ArrayList<>();
        /**
         * 注册的CallAdapter适配器工厂
         */
        private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
        /**
         * 回调时使用的执行器
         */
        private @Nullable
        Executor callbackExecutor;
        /**
         * 是否马上检查API接口的合法性
         */
        private boolean validateEagerly;

        Builder(Platform platform) {
            this.platform = platform;
        }

        public Builder() {
            this(Platform.get());
        }

        /**
         * 直接拷贝另外一个Retrofit实例的配置到Builder
         */
        Builder(Retrofit retrofit) {
            platform = Platform.get();
            callFactory = retrofit.callFactory;
            baseUrl = retrofit.baseUrl;

            // Do not add the default BuiltIntConverters and platform-aware converters added by build().
            for (int i = 1,
                 size = retrofit.converterFactories.size() - platform.defaultConverterFactoriesSize();
                 i < size;
                 i++) {
                converterFactories.add(retrofit.converterFactories.get(i));
            }

            // Do not add the default, platform-aware call adapters added by build().
            for (int i = 0,
                 size =
                 retrofit.callAdapterFactories.size() - platform.defaultCallAdapterFactoriesSize();
                 i < size;
                 i++) {
                callAdapterFactories.add(retrofit.callAdapterFactories.get(i));
            }

            callbackExecutor = retrofit.callbackExecutor;
            validateEagerly = retrofit.validateEagerly;
        }

        /**
         * 设置OkHttpClient
         */
        public Builder client(OkHttpClient client) {
            return callFactory(Objects.requireNonNull(client, "client == null"));
        }

        /**
         * 设置OkHttpClient
         */
        public Builder callFactory(okhttp3.Call.Factory factory) {
            this.callFactory = Objects.requireNonNull(factory, "factory == null");
            return this;
        }

        /**
         * 配置URL类型的基础Url
         */
        public Builder baseUrl(URL baseUrl) {
            Objects.requireNonNull(baseUrl, "baseUrl == null");
            return baseUrl(HttpUrl.get(baseUrl.toString()));
        }

        /**
         * 配置String类型基础Url
         */
        public Builder baseUrl(String baseUrl) {
            Objects.requireNonNull(baseUrl, "baseUrl == null");
            return baseUrl(HttpUrl.get(baseUrl));
        }

        /**
         * 配置HttpUrl类型基础Url
         */
        public Builder baseUrl(HttpUrl baseUrl) {
            Objects.requireNonNull(baseUrl, "baseUrl == null");
            List<String> pathSegments = baseUrl.pathSegments();
            if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
                throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
            }
            this.baseUrl = baseUrl;
            return this;
        }

        /**
         * 添加一个Converter工厂
         */
        public Builder addConverterFactory(Converter.Factory factory) {
            converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
            return this;
        }

        /**
         * 添加一个CallAdapter工厂
         */
        public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
            callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
            return this;
        }

        /**
         * 配置回调执行器
         */
        public Builder callbackExecutor(Executor executor) {
            this.callbackExecutor = Objects.requireNonNull(executor, "executor == null");
            return this;
        }

        /**
         * 返回注册的CallAdapter工厂
         */
        public List<CallAdapter.Factory> callAdapterFactories() {
            return this.callAdapterFactories;
        }

        /**
         * 返回注册的Converter工厂
         */
        public List<Converter.Factory> converterFactories() {
            return this.converterFactories;
        }

        /**
         * 配置是否迫切校验接口上的方法
         */
        public Builder validateEagerly(boolean validateEagerly) {
            this.validateEagerly = validateEagerly;
            return this;
        }

        /**
         * 构建一个Retrofit实例
         */
        public Retrofit build() {
            //必须设置基础路径
            if (baseUrl == null) {
                throw new IllegalStateException("Base URL required.");
            }

            okhttp3.Call.Factory callFactory = this.callFactory;
            //外部没有配置，创建默认配置的OkHttpClient
            if (callFactory == null) {
                callFactory = new OkHttpClient();
            }

            //外部没有配置，自动从平台中拿
            Executor callbackExecutor = this.callbackExecutor;
            if (callbackExecutor == null) {
                callbackExecutor = platform.defaultCallbackExecutor();
            }

            // Make a defensive copy of the adapters and add the default Call adapter.
            List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
            //默认添加一个CallAdapter工厂，支持Call类型的返回值
            callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

            // Make a defensive copy of the converters.
            List<Converter.Factory> converterFactories =
                    new ArrayList<>(
                            1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

            // Add the built-in converter factory first. This prevents overriding its behavior but also
            // ensures correct behavior when using converters that consume all types.
            //默认添加一个Converter工厂，支持返回值的泛型为RequestBody类型
            converterFactories.add(new BuiltInConverters());
            converterFactories.addAll(this.converterFactories);
            //从平台中拿Converter工厂，不同平台可以有不同的支持，如果支持Java8，则会添加一个Option类型的支持
            converterFactories.addAll(platform.defaultConverterFactories());
            //创建实例
            return new Retrofit(
                    callFactory,
                    baseUrl,
                    unmodifiableList(converterFactories),
                    unmodifiableList(callAdapterFactories),
                    callbackExecutor,
                    validateEagerly);
        }
    }
}
